#include "fat.h"

#include <mm/valloc.h>
#include <device/block.h>

int fat_unicode_name_parse(char* dst, ldire_t* src) {
    char tmp[13] = { 0 };
    size_t len = 0, i = 0;

    for (i = 0; i < 10; i += 2) {
        tmp[len++] = src->unicode_name1[i];
    }
    for (i = 0; i < 12; i += 2) {
        tmp[len++] = src->unicode_name2[i];
    }
    for (i = 0; i < 4; i += 2) {
        tmp[len++] = src->unicode_name3[i];
    }

    for (i = 0; i < len && tmp[i]; i++) {
        dst[i] = tmp[i];
    }
    dst[i] = 0;
    return i;
}

int fat_name_compare(dentry_t* dentry, ldire_t* name) {
    size_t len = dentry->name.len;
    char buf[14] = { 0 };

    size_t n = fat_unicode_name_parse(buf, name);
    if (len != n) {
        return 0;
    }

    while (len--) {
        if (dentry->name.val[len] != buf[len]) {
            return 0;
        }
    }
    return 1;
}

int fat_get_dtype(u8 flag) {
    if (flag & DIRECTORY) {
        return DT_DIR;
    }
    return DT_FILE;
}

int fat_next_clus(inode_t* inode, u32 cluster, u32* next_cluster) {
    return fat_nextN_clus(inode, cluster, 1, next_cluster);
}

int fat_nextN_clus(inode_t* inode, u32 cluster, u32 offset_clus, u32* next_cluster) {

    fat_sb_t* sb = inode->sb->data;
    blk_dev_t* bdev = inode->sb->dev->impl;
    u32 limit = sb->max_cluster;
    int errno = 0;

    if (offset_clus == 0) {
        *next_cluster = cluster;
        return 0;
    }

    // cluster -= sb->root_cluster;
    if (cluster >= limit) {
        errno = ENXIO;
        goto out;
    }

    u32* buf = valloc(sb->cluster_size);
    if (!buf) {
        errno = ENOMEM;
        goto out;
    }
    // 512 byte contains 128 cluster numbers
    u64 nblk = sb->fat_start + bdev->start_lba + (cluster >> 7);
    u64 last_blk = 0;
    u32 offset = 0;
    while (offset < offset_clus) {
        if (nblk != last_blk) {
            last_blk = nblk;
            errno = bdev->block_read(bdev, buf, nblk, 1);
            if (errno < 1) {
                errno = EIO;
                goto out;
            }
        }
        cluster = buf[cluster & 0x7f] & 0x0FFFFFFF;

        nblk = sb->fat_start + bdev->start_lba + (cluster >> 7);
        offset++;
    }
    *next_cluster = cluster;
    errno = offset;
out:
    vfree(buf);
    return errno;
}

int fat_get_cluster(inode_t* inode, u32 cluster, void* buf) {

    fat_sb_t* sb = inode->sb->data;
    blk_dev_t* bdev = inode->sb->dev->impl;
    u32 limit = sb->max_cluster;
    cluster -= sb->root_cluster;

    if (cluster >= limit) {
        return ENXIO;
    }

    int errno = 0, nblk = sb->data_start + bdev->start_lba + cluster;
    errno = bdev->block_read(bdev, buf, nblk, 1);
    return errno;
}